package com.coisshi.base.module.oss.service.impl;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.PolicyConditions;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import com.coisshi.base.common.exception.BizException;
import com.coisshi.base.framework.config.properties.AliyunOssProperties;
import com.coisshi.base.framework.config.properties.CloudStorageProperties;
import com.coisshi.base.module.oss.service.CloudStorageService;
import com.coisshi.base.module.oss.service.OssService;
import com.coisshi.base.module.oss.vo.OssPolicyResultVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.time.format.DateTimeFormatter;
import java.util.Date;

/**
 * OSS服务实现类
 *
 * @author coisshi
 * @date 2025/04/28
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AliyunOssServiceImpl implements CloudStorageService, OssService {

    // 默认过期时间（秒）
    private static final long DEFAULT_EXPIRE_TIME = 300;
    // 最大文件大小（1GB）
    private static final long MAX_FILE_SIZE = 1024 * 1024 * 1024;
    // 日期格式 - 年月日
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd");
    private final OSS ossClient;
    private final CloudStorageProperties cloudStorageProperties;

    /**
     * 获取OSS上传策略
     *
     * @return OSS策略信息
     */
    @Override
    public OssPolicyResultVo getPolicy(String dirPrefix) {
        AliyunOssProperties ossProperties = cloudStorageProperties.getAliyun();
        validateOssProperties(ossProperties);
        try {
            String endpoint = ossProperties.getEndpoint();
            String basePath = ossProperties.getDir();

            // 目录配置
            String dir = basePath + dirPrefix;

            // 处理主机地址
            String host = formatHost(endpoint, ossProperties);

            // 签名有效时间（5分钟）
            long expireEndTime = System.currentTimeMillis() + DEFAULT_EXPIRE_TIME * 1000;
            Date expiration = new Date(expireEndTime);

            // 策略条件
            PolicyConditions policyConditions = createPolicyConditions(dir);

            // 生成策略
            String postPolicy = ossClient.generatePostPolicy(expiration, policyConditions);
            byte[] binaryData = postPolicy.getBytes();
            String encodedPolicy = java.util.Base64.getEncoder().encodeToString(binaryData);
            String signature = ossClient.calculatePostSignature(postPolicy);

            // 封装返回结果
            return createPolicyResult(encodedPolicy, signature, dir, host, expireEndTime, ossProperties);
        } catch (OSSException e) {
            log.error("获取OSS策略失败: {}", e.getMessage(), e);
            throw new BizException("获取OSS策略失败: " + e.getMessage());
        } catch (Exception e) {
            log.error("获取OSS策略发生未知错误: {}", e.getMessage(), e);
            throw new BizException("获取OSS策略失败，请稍后重试");
        }
    }

    /**
     * 上传文件到OSS
     *
     * @param file       文件
     * @param objectName 对象名称，例如：images/2023/avatar.jpg
     * @return 文件URL
     * @throws BizException 业务异常
     */
    @Override
    public String uploadFile(MultipartFile file, String objectName) throws BizException {
        AliyunOssProperties ossProperties = cloudStorageProperties.getAliyun();
        if (file == null || file.isEmpty()) {
            throw new BizException("上传文件不能为空");
        }

        // 如果未指定对象名，使用原始文件名根据文件类型生成结构化路径
        if (!StringUtils.hasText(objectName)) {
            objectName = generateStructuredObjectName(ossProperties.getDir(), file.getOriginalFilename(), file.getContentType());
        }

        try {
            return uploadFile(file.getInputStream(), objectName);
        } catch (IOException e) {
            log.error("读取上传文件失败: {}", e.getMessage(), e);
            throw new BizException("读取上传文件失败：" + e.getMessage());
        }
    }

    /**
     * 上传数据流到OSS
     *
     * @param inputStream 输入流
     * @param objectName  对象名称
     * @return 文件URL
     * @throws BizException 业务异常
     */
    @Override
    public String uploadFile(InputStream inputStream, String objectName) throws BizException {
        AliyunOssProperties ossProperties = cloudStorageProperties.getAliyun();
        validateOssProperties(ossProperties);

        if (inputStream == null) {
            throw new BizException("上传文件流不能为空");
        }

        if (!StringUtils.hasText(objectName)) {
            throw new BizException("对象名称不能为空");
        }

        try {
            // 上传文件
            PutObjectRequest putObjectRequest = new PutObjectRequest(
                    ossProperties.getBucketName(), objectName, inputStream);

            // 执行上传
            PutObjectResult result = ossClient.putObject(putObjectRequest);
            log.info("文件上传成功, ETag: {}, objectName: {}", result.getETag(), objectName);

            // 生成访问URL
            return generateFileUrl(objectName, ossProperties);
        } catch (OSSException e) {
            log.error("OSS上传失败: {}", e.getMessage(), e);
            throw new BizException("文件上传到OSS失败: " + e.getMessage());
        } catch (Exception e) {
            log.error("OSS上传发生未知错误: {}", e.getMessage(), e);
            throw new BizException("文件上传失败，请稍后重试");
        }
    }

    /**
     * 删除OSS文件
     *
     * @param objectName 对象名称
     * @throws BizException 业务异常
     */
    @Override
    public void deleteFile(String objectName) throws BizException {
        AliyunOssProperties ossProperties = cloudStorageProperties.getAliyun();
        validateOssProperties(ossProperties);

        if (!StringUtils.hasText(objectName)) {
            throw new BizException("删除的对象名称不能为空");
        }

        try {
            ossClient.deleteObject(ossProperties.getBucketName(), objectName);
            log.info("文件删除成功, objectName: {}", objectName);
        } catch (OSSException e) {
            log.error("OSS删除文件失败: {}", e.getMessage(), e);
            throw new BizException("删除OSS文件失败: " + e.getMessage());
        } catch (Exception e) {
            log.error("OSS删除文件发生未知错误: {}", e.getMessage(), e);
            throw new BizException("删除文件失败，请稍后重试");
        }
    }

    /**
     * 校验OSS配置参数
     */
    private void validateOssProperties(AliyunOssProperties ossProperties) {
        if (!StringUtils.hasText(ossProperties.getEndpoint())) {
            throw new BizException("OSS endpoint配置缺失");
        }
        if (!StringUtils.hasText(ossProperties.getAccessKeyId())) {
            throw new BizException("OSS accessKeyId配置缺失");
        }
        if (!StringUtils.hasText(ossProperties.getAccessKeySecret())) {
            throw new BizException("OSS accessKeySecret配置缺失");
        }
        if (!StringUtils.hasText(ossProperties.getBucketName())) {
            throw new BizException("OSS bucketName配置缺失");
        }
    }

    /**
     * 格式化主机地址
     */
    private String formatHost(String endpoint, AliyunOssProperties ossProperties) {
        return "https://" + ossProperties.getBucketName() + "." +
                endpoint.replace("https://", "").replace("http://", "");
    }

    /**
     * 创建策略条件
     */
    private PolicyConditions createPolicyConditions(String dir) {
        PolicyConditions policyConditions = new PolicyConditions();
        // 限制上传文件大小 (0-1GB)
        policyConditions.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, MAX_FILE_SIZE);
        // 设置上传文件的目录前缀
        if (StringUtils.hasText(dir)) {
            policyConditions.addConditionItem(PolicyConditions.COND_KEY, dir);
        }
        return policyConditions;
    }

    /**
     * 创建策略结果对象
     */
    private OssPolicyResultVo createPolicyResult(String encodedPolicy, String signature,
                                                 String dir, String host, long expireEndTime, AliyunOssProperties ossProperties) {
        OssPolicyResultVo policyResult = new OssPolicyResultVo();
        policyResult.setAccessKeyId(ossProperties.getAccessKeyId());
        policyResult.setPolicy(encodedPolicy);
        policyResult.setSignature(signature);
        policyResult.setDir(dir);
        policyResult.setHost(host);
        policyResult.setExpire(String.valueOf(expireEndTime / 1000));

        return policyResult;
    }

    /**
     * 生成结构化的对象名称，包含基础路径、文件类型子目录、日期目录和文件名
     *
     * @param basePath         基础路径
     * @param originalFilename 原始文件名
     * @param contentType      文件内容类型
     * @return 完整的对象名称
     */
    private String generateStructuredObjectName(String basePath, String originalFilename, String contentType) {
        // 可调用CloudStorageUtil.generateStructuredObjectName(basePath, originalFilename, contentType)
        return com.coisshi.base.module.oss.util.CloudStorageUtil.generateStructuredObjectName(basePath, originalFilename, contentType);
    }

    /**
     * 生成文件URL
     */
    private String generateFileUrl(String objectName, AliyunOssProperties ossProperties) {
        String endpoint = ossProperties.getEndpoint().replace("https://", "").replace("http://", "");
        return "https://" + ossProperties.getBucketName() + "." + endpoint + "/" + objectName;
    }
} 